
// Classe para recuperar estado do Tetragnatha apos quebra.

#include <iostream>
#include <cstdio>  // para abrir arquivo
#include <string>
#include <map>
#include <fstream>
#include <ctime>

#include "TetraRecovery.h"
#include "../classes_net/URL.h"
#include "../classes_net/URLFileDealer.h"
#include "../classes_dados/Hasher.h"
#include "../classes_dados/ManagerBerthier.h"


#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
#include <boost/tr1/memory.hpp>


using namespace std;


TetraRecovery::TetraRecovery(){
	
	pDisk[0] = fopen64 ("DiscoURL_P0.bin","ab+");  //Leitura e "escrita no final"
	pDisk[1] = fopen64 ("DiscoURL_P1.bin","ab+");  //Leitura e "escrita no final"
	pDisk[2] = fopen64 ("DiscoURL_P2.bin","ab+");  //Leitura e "escrita no final"
	pDisk[3] = fopen64 ("DiscoURL_P3.bin","ab+");  //Leitura e "escrita no final"
	pDisk[4] = fopen64 ("DiscoURL_P4.bin","ab+");  //Leitura e "escrita no final"
	pDisk[5] = fopen64 ("DiscoURL_P5.bin","ab+");  //Leitura e "escrita no final"
	
	for(int i=0; i<6; i++)
	    if (pDisk[i] == NULL){
	    	cout << "Erro ao abrir o arquivo de disco pDisk: "<< i << endl;
	    	exit(1);
	    }
	
    pSaveDiskReadPos[0] = fopen64 ("pSaveDiskReadPos_P0","r+");  //Leitura e escreve
    pSaveDiskReadPos[1] = fopen64 ("pSaveDiskReadPos_P1","r+");  //Leitura e escreve
    pSaveDiskReadPos[2] = fopen64 ("pSaveDiskReadPos_P2","r+");  //Leitura e escreve
    pSaveDiskReadPos[3] = fopen64 ("pSaveDiskReadPos_P3","r+");  //Leitura e escreve
    pSaveDiskReadPos[4] = fopen64 ("pSaveDiskReadPos_P4","r+");  //Leitura e escreve
    pSaveDiskReadPos[5] = fopen64 ("pSaveDiskReadPos_P5","r+");  //Leitura e escreve
    
    for(int i=0; i<6; i++)
	    if (pSaveDiskReadPos[i] == NULL){
	    	cout << "Erro ao abrir o arquivo de disco pSaveDiskReadPos: " << i << endl;
	    	exit(1);
	    }
    
    URLBuffer_IN = new _t_URL;
    URLBuffer_OUT = new _t_URL;	
}


TetraRecovery::~TetraRecovery(){
	for(int i=0; i<6; i++)	 
		if(pDisk[i] != NULL) fclose (pDisk[i]);
	
	for(int i=0; i<6; i++)
		if(pSaveDiskReadPos[i] != NULL) fclose (pSaveDiskReadPos[i]);
	
	delete URLBuffer_IN;
	delete URLBuffer_OUT;
}





URL TetraRecovery::readDisk(void){
	boost::mutex::scoped_lock urlDiskClassLock(urlDiskClassMutex);
//	URLBuffer_IN = new _t_URL;
//	SEEK_SET	Beginning of file
//	SEEK_CUR	Current position of the file pointer
//	SEEK_END	End of file
	long long pDiskOffset;
  	long long pDiskSize;
  	long long _savePos;
  	
  	int id;
  	for(id=0; id<6; id++){
  	  	// Tamanho do disco
  	  	pDiskSize = size(id);
  	  	//Recupera ultima posicao lida
  	  	_savePos  = pos(id);
  	  	//verifica se vazio
  	  	if(_savePos > pDiskSize) continue;
  	  	
  	  	pDiskOffset = _savePos;
  	  	_savePos = _savePos + 1;
  	  	
  	  	fseeko64 ( pDisk[id] , pDiskOffset*sizeof(_t_URL) , SEEK_SET);
  	  	fread(URLBuffer_IN, sizeof(_t_URL), 1, pDisk[id]);
  	  	tURL = URL(URLBuffer_IN->t_dominio, URLBuffer_IN->t_path, URLBuffer_IN->t_protocol );
  	  	
  	  	//Grava ultima pos lida
  	  	writepos(id, _savePos);
  	  
  	    return tURL;
  	}
  	  
	cout << "Todos os discos vazios..." << endl;
	tURL = URL("", "","");
	return tURL;  
//  delete URLBuffer_IN;
}




long long TetraRecovery::size(int id){
	long long pDiskSize;
	if(id < 0) id = 0;
	if(id > 5) id = 5;
  	fseeko64 (pDisk[id] , 0 , SEEK_END); //vai para o final
  	pDiskSize = ftello64 (pDisk[id]);  	// Pega o tamanho
  	pDiskSize = pDiskSize / sizeof(_t_URL);
  	return pDiskSize;
}


long long TetraRecovery::pos(int id){
	long long _save_pos;
	if(id < 0) id = 0;
	if(id > 5) id = 5;
  	rewind (pSaveDiskReadPos[id]);
  	fscanf (pSaveDiskReadPos[id], "%lld", &_save_pos);
  	return _save_pos;
}

void TetraRecovery::writepos(int id, long long _save_pos){
  	//Grava ultima pos lida
  	rewind (pSaveDiskReadPos[id]);
  	fprintf(pSaveDiskReadPos[id], "%lld", _save_pos); // aponta para proximo lugar a ser lido
  	fflush (pSaveDiskReadPos[id]);
}

long long TetraRecovery::totalNewURLs(void){
	long long tmTotal;
	long long tmTemp;
	
	tmTotal = 0;
	
	for(int i=0; i<6; i++){
		tmTemp = size(i) - pos(i);
		if(tmTemp >= 0) tmTotal += tmTemp;		
	}
	
	return tmTotal;
}


void TetraRecovery::recovery(void){
	
	//estrutura para Hash Domain
	std::tr1::shared_ptr<ManagerBerthier> mb = ManagerBerthier::getInstance();
	//estrutura para Hash de URLs visitadas
	std::tr1::shared_ptr<URLFileDealer> urlFileDealer = URLFileDealer::getInstance();
	
	long long save_pos_disk;
	URL tempUrl;
	int id = 0;
	string urlHash = "";
	long long singleURLs = 0;
	
	for(id = 0; id < 6; id++){
		save_pos_disk = pos(id);
		cout << "Recuperando Disco: " << id << " Total: " << save_pos_disk-1 << " URLs" << endl;
		for(long long posd=0; posd < save_pos_disk; posd++){
	  	  	fseeko64 ( pDisk[id], posd*sizeof(_t_URL) , SEEK_SET);
	  	  	fread(URLBuffer_IN, sizeof(_t_URL), 1, pDisk[id]);
	  	    tempUrl = URL(URLBuffer_IN->t_dominio, URLBuffer_IN->t_path, URLBuffer_IN->t_protocol );

	  	    urlHash = Hasher::md5(tempUrl.toString());	
			if  (urlHashMapRecovery[urlHash] == false){ //verifica se tem 
				urlHashMapRecovery[urlHash] = true;     //marca que agora tem
				singleURLs++;

		  	    // Faz algo com o tempURL !!!
				// Baggio ?
				mb->initHashDomain(tempUrl);
				urlFileDealer->initURLHashMap(tempUrl);
			}	
//		    cout << "Lendo Disco: " << id << "  ... " << posd << " de " << save_pos_disk << endl;
//		    cout << "Recuperando: "<< tempUrl.toString() << endl;
		}	
		cout << "---> Total nao repetido: " << singleURLs << endl;
		singleURLs = 0;
	}
	urlHashMapRecovery.clear(); //limpa tudo do map
}


void TetraRecovery::contaPaginasDisco(void){
	long long save_pos_disk;
	long long disk_size;
	URL tempUrl;
	int id = 0;
	string urlHash = "";
	long long singleURLs = 0;
	
	cout << "Modo especial:  Contando links do Disco " << endl;
	// Ensinando o Tetragnatha a contar...
	for(id = 0; id < 6; id++){
		save_pos_disk = pos(id);
		disk_size = size(id);
		cout << "Disco " << id << " -> Total Lido: " << save_pos_disk-1 << " URLs" << endl;
		cout << "Tamanho do Disco: " << disk_size << " URLs" << endl;
		for(long long posd=0; posd < save_pos_disk; posd++){
	  	  	fseeko64 ( pDisk[id], posd*sizeof(_t_URL) , SEEK_SET);
	  	  	fread(URLBuffer_IN, sizeof(_t_URL), 1, pDisk[id]);
	  	    tempUrl = URL(URLBuffer_IN->t_dominio, URLBuffer_IN->t_path, URLBuffer_IN->t_protocol );

	  	    urlHash = Hasher::md5(tempUrl.toString());	
			if  (urlHashMapRecovery[urlHash] == false){ //verifica se tem 
				urlHashMapRecovery[urlHash] = true;     //marca que agora tem
				singleURLs++;
			}	
//		    cout << "Lendo Disco: " << id << "  ... " << posd << " de " << save_pos_disk << endl;
//		    cout << "Recuperando: "<< tempUrl.toString() << endl;
		}	
		cout << "---> Total nao repetido: " << singleURLs << endl;
		singleURLs = 0;
	}
	urlHashMapRecovery.clear(); //limpa tudo do map
	
	for(id = 0; id < 6; id++){
		save_pos_disk = pos(id);
		disk_size = size(id);
		cout << "Disco :" << id << endl;
		for(long long posd=save_pos_disk; posd < disk_size; posd++){
			
	  	  	fseeko64 ( pDisk[id], posd*sizeof(_t_URL) , SEEK_SET);
	  	  	fread(URLBuffer_IN, sizeof(_t_URL), 1, pDisk[id]);
	  	  	
	  	    tempUrl = URL(URLBuffer_IN->t_dominio, URLBuffer_IN->t_path, URLBuffer_IN->t_protocol );

	  	    urlHash = Hasher::md5(tempUrl.toString());	
			if  (urlHashMapRecovery[urlHash] == false){ //verifica se tem 
				urlHashMapRecovery[urlHash] = true;     //marca que agora tem
				singleURLs++;
			}	
		}	
		cout << "---> Total nao repetido ainda nao lido: " << singleURLs << endl;
		singleURLs = 0;
	}	
	cout << "->>>   FIM !!!  " << endl;	
}


void TetraRecovery::currentState(int _now){
	long long save_pos_disk;
	URL tempUrl;
	int id = 0;
	string urlHash = "";
	long long singleURLs = 0;
	time_t tempo;
	time(&tempo);
	//Gera:  tempo  deltaTempo numero_P0 numero_P1 numero_P2 numero_P3 
	//                         numero_P4 numero_P5  endl

	cout << tempo;
	cout << " " << tempo - _now;
	for(id = 0; id < 6; id++){
		save_pos_disk = pos(id);
		for(long long posd=0; posd < save_pos_disk; posd++){
	  	  	fseeko64 ( pDisk[id], posd*sizeof(_t_URL) , SEEK_SET);
	  	  	fread(URLBuffer_IN, sizeof(_t_URL), 1, pDisk[id]);
	  	    tempUrl = URL(URLBuffer_IN->t_dominio, URLBuffer_IN->t_path, URLBuffer_IN->t_protocol );
	  	    urlHash = Hasher::md5(tempUrl.toString());	
			if  (urlHashMapRecovery[urlHash] == false){ //verifica se tem 
				urlHashMapRecovery[urlHash] = true;     //marca que agora tem
				singleURLs++;
			}	
		}	
		cout << " " << singleURLs;
		singleURLs = 0;
	}
	cout << endl;
	urlHashMapRecovery.clear(); //limpa tudo do map
}




	
	